cells = read.csv('/Users/Jonnie/Desktop/CERN/train_100_events/event000001000-cells.csv')
hits = read.csv('/Users/Jonnie/Desktop/CERN/train_100_events/event000001000-hits.csv')
particles = read.csv('/Users/Jonnie/Desktop/CERN/train_100_events/event000001000-particles.csv')
truth = read.csv('/Users/Jonnie/Desktop/CERN/train_100_events/event000001000-truth.csv')
Change types
for (i in 1:length(cells)){
print(paste('cells:',names(cells)[i],':',class(cells[,i])))
}
for (i in 1:length(hits)){
print(paste('hits:', names(hits)[i],':',class(hits[,i])))
}
for (i in 1:length(particles)){
print(paste('particles:', names(particles)[i],':',class(particles[,i])))
}
for (i in 1:length(truth)){
print(paste('truth:', names(truth)[i],':',class(truth[,i])))
}
cells$hit_id = factor(cells$hit_id)
cells$ch0 = as.numeric(cells$ch0)
cells$ch1 = as.numeric(cells$ch1)
hits$hit_id = factor(hits$hit_id)
hits$volume_id = factor(hits$volume_id)
hits$layer_id = factor(hits$layer_id)
hits$module_id = factor(hits$module_id)
particles$particle_id = factor(particles$particle_id)
truth$particle_id = factor(truth$particle_id)
Particles + truth
particles_truth <- merge(particles,truth,by="particle_id")
particles_truth$net_x = abs(particles_truth$vx - particles_truth$tx)
particles_truth$net_y = abs(particles_truth$vy - particles_truth$ty)
particles_truth$net_z = abs(particles_truth$vz - particles_truth$tz)
particles_truth$two_norm = sqrt(particles_truth$net_x^2 + particles_truth$net_y^2 + particles_truth$net_z^2)
orginal and last position
net displacement
df = particles_truth[1,]
df = df[-1,]
i = 1
while (i < nrow(particles_truth)){
key = particles_truth$particle_id[i]
test = particles_truth[particles_truth$particle_id == key,]
df = rbind(df,test[nrow(test),])
i = i + nrow(test)
if(nrow(df) > 500){
break
}
}
df$q = factor(df$q)
class(df$q)
[1] "factor"
library(ggplot2)
library(latex2exp)
df_plot = qplot(df$two_norm,bins =15 )
df_plot+ labs(title = "Distribution of Total displacement") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$last position - origin positon$||_2$"))

Split positive and negative charge data
pos_charge = df[df$q == 1,]
neg_charge = df[df$q ==-1,]
ggplot() + geom_histogram(data = pos_charge, aes(x = two_norm,color = 'blue'),bins = 15) + geom_histogram(data = neg_charge, aes(x = two_norm,color = 'red'),bins = 15) + labs(title = "Distribution of Total displacement: Positive vs Negatively charged") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$last position - origin positon$||_2$"))

Run test
mean(neg_charge$two_norm)
[1] 722.9007
mean(pos_charge$two_norm)
[1] 756.4089
wilcox.test(neg_charge$two_norm, pos_charge$two_norm)
Wilcoxon rank sum test with continuity correction
data: neg_charge$two_norm and pos_charge$two_norm
W = 30523, p-value = 0.6656
alternative hypothesis: true location shift is not equal to 0
Indeed distributions are similar when normalized
ggplot() + geom_histogram(data = pos_charge, aes(x = two_norm,y=..count../sum(..count..),color = 'blue'),bins = 15) + geom_histogram(data = neg_charge, aes(x = two_norm,y=..count../sum(..count..),color = 'red'),bins = 15) + labs(title = "Normalized Distribution: Positive vs Negatively charged") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$last position - origin positon$||_2$"))

Run chi-square to see if two_norm data follows expoenential - Fail
df_test = layer_data(df_plot,1)
p2 = pexp(df_test$xmax,rate = 1/mean(df$two_norm)) - pexp(pmax(df_test$xmin,0),rate = 1/mean(df$two_norm))
chisq.test(df_test$count, p = p2, rescale.p = TRUE)
Chi-squared approximation may be incorrect
Chi-squared test for given probabilities
data: df_test$count
X-squared = 87.009, df = 29, p-value = 1.043e-07
first displacement
df = particles_truth[1,]
df = df[-1,]
i = 1
while (i < nrow(particles_truth)){
key = particles_truth$particle_id[i]
test = particles_truth[particles_truth$particle_id == key,]
df = rbind(df, test[1,])
i = i + nrow(test)
if(nrow(df) > 1000){
break
}
}
df$q = factor(df$q)
class(df$q)
[1] "factor"
library(ggplot2)
df_plot = qplot(df$two_norm,bins =15 )
df_plot + labs(title = "Distribution of first displacement") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$first hit position - origin positon$||_2$"))

Split positive and negative charge data
pos_charge = df[df$q == 1,]
neg_charge = df[df$q ==-1,]
nrow(pos_charge)
[1] 528
nrow(neg_charge)
[1] 473
ggplot() + geom_histogram(data = pos_charge, aes(x = two_norm,color = 'blue'),bins = 15) + geom_histogram(data = neg_charge, aes(x = two_norm,color = 'red'),bins = 15) + labs(title = "Distribution of first displacement: Positive vs Negatively charged") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$first hit position - origin positon$||_2$"))

Run test
mean(neg_charge$two_norm)
[1] 781.3767
mean(pos_charge$two_norm)
[1] 780.207
wilcox.test(neg_charge$two_norm, pos_charge$two_norm)
Wilcoxon rank sum test with continuity correction
data: neg_charge$two_norm and pos_charge$two_norm
W = 127020, p-value = 0.638
alternative hypothesis: true location shift is not equal to 0
Normalized
ggplot() + geom_histogram(data = pos_charge, aes(x = two_norm,y=..count../sum(..count..),color = 'blue'),bins = 15) + geom_histogram(data = neg_charge, aes(x = two_norm,y=..count../sum(..count..),color = 'red'),bins = 15) + labs(title = "Normalized Distribution: Positive vs Negatively charged") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$first hit position - origin positon$||_2$"))

df_test = layer_data(df_plot,1)
p2 = pexp(df_test$xmax,rate = 1/mean(df$two_norm)) - pexp(pmax(df_test$xmin,0),rate = 1/mean(df$two_norm))
chisq.test(df_test$count, p = p2, rescale.p = TRUE)
Chi-squared approximation may be incorrect
Chi-squared test for given probabilities
data: df_test$count
X-squared = 133.15, df = 29, p-value = 2.723e-15
Same results for First displacement
Unique values in each event
number_particles = c()
for( i in 0:99){
event = 1000 + i
event_string = toString(event)
link = paste0('/Users/Jonnie/Desktop/CERN/train_100_events/event00000',event_string,'-particles.csv')
particles2 = read.csv(link)
number_particles = c(number_particles,nrow(particles2))
}
number_in_event = qplot(number_particles,bins = 10)
number_in_event + labs(title = "Number of particles across events") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("Number of particles"))

Chi-square
event_test = layer_data(number_in_event,1)
p1 = pnorm(event_test$xmax,mean = mean(number_particles),sd = sd(number_particles)) - pnorm(event_test$xmin,mean = mean(number_particles), sd = sd(number_particles))
chisq.test(event_test$count, p = p1, rescale.p = TRUE)
Chi-squared approximation may be incorrect
Chi-squared test for given probabilities
data: event_test$count
X-squared = 6.9443, df = 9, p-value = 0.6429
KS-test
ks.test(number_particles,"pnorm",mean(number_particles),sd(number_particles))
ties should not be present for the Kolmogorov-Smirnov test
One-sample Kolmogorov-Smirnov test
data: number_particles
D = 0.083973, p-value = 0.4811
alternative hypothesis: two-sided
ITS NORMAL
Particles with no hits
Check numbers of particle ids with no data for truth
length(unique(particles$particle_id))
[1] 12263
length(unique(particles_truth$particle_id))
[1] 10565
length(setdiff(particles$particle_id,truth$particle_id))
[1] 1698
no_truth = particles[particles$particle_id %in% setdiff(particles$particle_id,truth$particle_id),]
nrow(no_truth)
[1] 1698
Test proportion of charge in particles with no hits
qplot(particles_truth$q,bins = 10) + labs(title = "Charge of particles for those with hits") + theme(plot.title = element_text(hjust = 0.5)) +xlab("Charge of particle")

qplot(no_truth$q,bins = 10)+ labs(title = "Charge of particles for those with no hits") + theme(plot.title = element_text(hjust = 0.5)) +xlab("Charge of particle")

no_truth_prop = nrow(no_truth[no_truth$q == 1,])/length(no_truth$q)
yes_truth_prop = nrow(particles_truth[particles_truth$q == 1,])/length(particles_truth$q)
paste0('no_truth_prop: ',no_truth_prop)
[1] "no_truth_prop: 0.60718492343934"
paste0('yes_truth_prop: ',yes_truth_prop)
[1] "yes_truth_prop: 0.534591742897246"
Z- Test of proportions
p_e = nrow(particles[particles$q == 1,])/length(particles$q)
z_observed = (no_truth_prop - yes_truth_prop)/(sqrt((length(particles$q)*p_e*(1-p_e))/(length(no_truth$q)*length(particles_truth$q))))
paste0('Z score: ',z_observed)
[1] "Z score: 17.464992436606"
paste0('p-value: ',1-pnorm(z_observed))
[1] "p-value: 0"
AHA! Proportions are different! There is more positively charged particles (higher proportion of positively charged particles) in the set where there are no hits!
Visualizing starting positions of particles with 0 hits
library(plotly)
packageVersion('plotly')
[1] ‘4.7.1’
plot_ly(no_truth,x = ~vx,y = ~vy,z = ~vz,color = ~q,colors =c('blue','red') )
No trace type specified:
Based on info supplied, a 'scatter3d' trace seems appropriate.
Read more about this trace type -> https://plot.ly/r/reference/#scatter3d
No scatter3d mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
No trace type specified:
Based on info supplied, a 'scatter3d' trace seems appropriate.
Read more about this trace type -> https://plot.ly/r/reference/#scatter3d
No scatter3d mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Momentum of particles with no hits
no_truth$Mom_norm = sqrt(no_truth$px^2 + no_truth$py^2 + no_truth$pz^2)
particles_truth$Mom_norm = sqrt(particles_truth$px^2 + particles_truth$py^2 + particles_truth$pz^2)
qplot(no_truth$Mom_norm,bins = 10) + labs(title = "Distribution of momentum for no hits") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$Momentum$||_2$"))

qplot(particles_truth$Mom_norm,bins = 10) + labs(title = "Distribution of momentum for particles with hits") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$Momentum$||_2$"))

paste0('no_truth: ',mean(no_truth$Mom_norm))
[1] "no_truth: 1.45198626094505"
paste0('particles_truth: ',mean(particles_truth$Mom_norm))
[1] "particles_truth: 3.8282194082865"
Momentum norm of particles seem to be around 0 regardless of having any hits or not
Run test
wilcox.test(no_truth$Mom_norm, particles_truth$Mom_norm)
Wilcoxon rank sum test with continuity correction
data: no_truth$Mom_norm and particles_truth$Mom_norm
W = 36322000, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0
Momentum predicting nhits
mom_pred_nhits = lm(data = particles, nhits~Mom_norm)
summary(mom_pred_nhits)
Call:
lm(formula = nhits ~ Mom_norm, data = particles)
Residuals:
Min 1Q Median 3Q Max
-53.439 -4.238 1.859 3.825 10.909
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 8.053854 0.048705 165.36 <2e-16 ***
Mom_norm 0.119943 0.006206 19.33 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 4.959 on 12261 degrees of freedom
Multiple R-squared: 0.02956, Adjusted R-squared: 0.02948
F-statistic: 373.5 on 1 and 12261 DF, p-value: < 2.2e-16
temp_var <- predict(mom_pred_nhits, interval="prediction")
predictions on current data refer to _future_ responses
new_df <- cbind(particles, temp_var)
bob = ggplot(data = new_df , aes(x=Mom_norm, y=nhits)) +
geom_point() +
geom_line(aes(y=lwr), color = "red", linetype = "dashed")+
geom_line(aes(y=upr), color = "red", linetype = "dashed")+
geom_smooth(method=lm,se = TRUE) + labs(title = "Momentum predict nhits") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$Momentum$||_2$"))
bob

Momentum horrendous at predicting nhits since it clusters at 0 regardless of nhits
Zoom in perhaps?
particles_mNorm_less50= particles[particles$Mom_norm < 50,]
mom_pred_nhits = lm(data = particles_mNorm_less50, nhits~Mom_norm)
summary(mom_pred_nhits)
Call:
lm(formula = nhits ~ Mom_norm, data = particles_mNorm_less50)
Residuals:
Min 1Q Median 3Q Max
-18.227 -4.399 1.819 3.892 11.218
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.706871 0.051387 149.98 <2e-16 ***
Mom_norm 0.243628 0.008963 27.18 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 4.889 on 12240 degrees of freedom
Multiple R-squared: 0.05692, Adjusted R-squared: 0.05685
F-statistic: 738.8 on 1 and 12240 DF, p-value: < 2.2e-16
temp_var <- predict(mom_pred_nhits, interval="prediction")
predictions on current data refer to _future_ responses
new_df <- cbind(particles_mNorm_less50, temp_var)
bob = ggplot(data = new_df , aes(x=Mom_norm, y=nhits)) +
geom_point() +
geom_line(aes(y=lwr), color = "red", linetype = "dashed")+
geom_line(aes(y=upr), color = "red", linetype = "dashed")+
geom_smooth(method=lm,se = TRUE) + labs(title = "Momentum predict nhits: Zoom") + theme(plot.title = element_text(hjust = 0.5)) +xlab(TeX("$||$Momentum$||_2$"))
bob

Find modes
test = hist(particles$nhits,breaks = 20);

mode1 = test$mids[test$counts == max(test$counts)]
mode2 = test$mids[test$counts == sort(test$counts,partial = length(test$counts) - 1)[length(test$counts)-1]]
midpoint = (mode1 + mode2)/2
Ignore plot above
ggplot(data = particles , aes(x=nhits)) + geom_histogram(bins = 10) + geom_vline(aes(xintercept = midpoint,color = 'midpoint')) + geom_vline(aes(xintercept = mode1,color = 'mode1')) + geom_vline(aes(xintercept = mode2,color = 'mode2') ) + labs(title = "Distribution of nhits") + theme(plot.title = element_text(hjust = 0.5)) + scale_color_manual(name = "Labels", values = c(midpoint = "blue", mode1 = 'red', mode2 = "green"))

Chop em
expo_side = particles[particles$nhits < midpoint,]$nhits
norm_side = particles[particles$nhits > midpoint,]$nhits
expo_plot = qplot(expo_side,bins= 7)
norm_plot = qplot(norm_side,bins=7)
expo_plot + labs(title = "Exponential side") + theme(plot.title = element_text(hjust = 0.5))+xlab('nhits')

norm_plot+ labs(title = "Normal side") + theme(plot.title = element_text(hjust = 0.5))+xlab('nhits')

norm_test = layer_data(norm_plot,1)
p1 = pnorm(norm_test$xmax,mean = mean(norm_side),sd = sd(norm_side)) - pnorm(norm_test$xmin,mean = mean(norm_side), sd = sd(norm_side))
chisq.test(norm_test$count, p = p1, rescale.p = TRUE)
Chi-squared test for given probabilities
data: norm_test$count
X-squared = 552.69, df = 5, p-value < 2.2e-16
expo_test = layer_data(expo_plot,1)
p2 = pexp(expo_test$xmax,rate = 1/mean(expo_side)) - pexp(pmax(expo_test$xmin,0),rate = 1/mean(expo_side))
chisq.test(expo_test$count, p = p2, rescale.p = TRUE)
Chi-squared test for given probabilities
data: expo_test$count
X-squared = 2909.9, df = 6, p-value < 2.2e-16
KS test
ex = rexp(10000, rate = 1/mean(expo_side))
ks.test(ex,"pexp",expo_side)
One-sample Kolmogorov-Smirnov test
data: ex
D = 0.9995, p-value < 2.2e-16
alternative hypothesis: two-sided
FAILURE AGAIN
Kurtosis Test
library('moments')
kurtosis(expo_side)
[1] 2.478743
ex_set = c()
for (i in 1:1000){
ex = rexp(1000, rate = 1/mean(expo_side))
ex_k = kurtosis(ex)
ex_set = c(ex_set,ex_k)
}
qplot(ex_set,bins = 10) + geom_vline(aes(xintercept = kurtosis(expo_side),color = 'sample_value')) + scale_color_manual(name = "Labels", values = c(sample_value = "blue")) + labs(title = "Kurtosis Distribution of exponential with rate of sample") + theme(plot.title = element_text(hjust = 0.5))+xlab('Kurtosis')

kurtosis(norm_side)
[1] 2.908081
nm_set = c()
for (i in 1:1000){
nm = rnorm(10000, mean = mean(norm_side), sd = sd(norm_side))
nm_k = kurtosis(nm)
nm_set = c(nm_set,nm_k)
}
qplot(nm_set,bins = 10) + geom_vline(aes(xintercept = kurtosis(norm_side),color = 'sample_value')) + scale_color_manual(name = "Labels", values = c(sample_value = "blue")) + labs(title = "Kurtosis Distribution of Normal with parameters of sample") + theme(plot.title = element_text(hjust = 0.5))+xlab('Kurtosis')

Sum of values by particle_id
particles_value <- merge(particles_truth,cells,by="hit_id")
particles_value = aggregate(value ~ hit_id + particle_id+q,data = particles_value,FUN = sum)
particles_value = aggregate(value ~ particle_id+q,data = particles_value,FUN = sum)
value_plot = qplot(particles_value$value,bins = 10)
value_plot + labs(title = "Distribution of sum of particle values by ID") + theme(plot.title = element_text(hjust = 0.5))+xlab('Particle values')

Chi-square test
value_test = layer_data(value_plot,1)
p2 = pexp(value_test$xmax,rate = 1/mean(particles_value$value)) - pexp(pmax(value_test$xmin,0),rate = 1/mean(particles_value$value))
chisq.test(value_test$count, p = p2, rescale.p = TRUE)
Chi-squared approximation may be incorrect
Chi-squared test for given probabilities
data: value_test$count
X-squared = 159.1, df = 9, p-value < 2.2e-16
KS test
ex = rexp(10000, rate = 1/mean(particles_value$value))
ks.test(ex,"pexp",particles_value$value)
longer object length is not a multiple of shorter object length
One-sample Kolmogorov-Smirnov test
data: ex
D = 0.99256, p-value < 2.2e-16
alternative hypothesis: two-sided
neg_value = particles_value[particles_value$q == -1,]
pos_value = particles_value[particles_value$q ==1,]
qplot(neg_value$value,bins = 15)+ labs(title = "Distribution for negative charge") + theme(plot.title = element_text(hjust = 0.5))+xlab('Particle value')

qplot(pos_value$value,bins = 15)+ labs(title = "Distribution for positive charge") + theme(plot.title = element_text(hjust = 0.5))+xlab('Particle value')

Test if means are diff
mean(neg_value$value)
[1] 16.46946
mean(pos_value$value)
[1] 17.92995
wilcox.test(neg_value$value, pos_value$value)
Wilcoxon rank sum test with continuity correction
data: neg_value$value and pos_value$value
W = 13427000, p-value = 0.008229
alternative hypothesis: true location shift is not equal to 0
ggplot() + geom_histogram(data = pos_value, aes(x = value,color = 'blue'),bins = 15) + geom_histogram(data = neg_value, aes(x = value,color = 'red'),bins = 15)+ labs(title = "Positive and negative side by side") + theme(plot.title = element_text(hjust = 0.5))+xlab('Particle value')

sum of all values in all events
number_values = c()
for( i in 0:99){
event = 1000 + i
event_string = toString(event)
link = paste0('/Users/Jonnie/Desktop/CERN/train_100_events/event00000',event_string,'-cells.csv')
cells2 = read.csv(link)
number_values = c(number_values,sum(cells2$value))
}
values_plot = qplot(number_values,bins = 15)
values_plot+ labs(title = "Distribution of sum of all values per event") + theme(plot.title = element_text(hjust = 0.5))+xlab('Values per event')

Chi-square
values_test = layer_data(values_plot,1)
p1 = pnorm(values_test$xmax,mean = mean(number_values),sd = sd(number_values)) - pnorm(values_test$xmin,mean = mean(number_values), sd = sd(number_values))
chisq.test(values_test$count, p = p1, rescale.p = TRUE)
Chi-squared approximation may be incorrect
Chi-squared test for given probabilities
data: values_test$count
X-squared = 18.396, df = 14, p-value = 0.1893
KS-test
ks.test(number_values,"pnorm",mean(number_values),sd(number_values))
One-sample Kolmogorov-Smirnov test
data: number_values
D = 0.082207, p-value = 0.5087
alternative hypothesis: two-sided
ggplot(data = data.frame(values = number_values),aes(sample = values)) + stat_qq() + stat_qq_line(color = "red") + labs(title = "QQ- Norm") + theme(plot.title = element_text(hjust = 0.5))

LS0tCnRpdGxlOiAiTWF0aCAxODkgZmluYWwiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgpgYGB7cn0KY2VsbHMgPSByZWFkLmNzdignL1VzZXJzL0pvbm5pZS9EZXNrdG9wL0NFUk4vdHJhaW5fMTAwX2V2ZW50cy9ldmVudDAwMDAwMTAwMC1jZWxscy5jc3YnKQpoaXRzID0gcmVhZC5jc3YoJy9Vc2Vycy9Kb25uaWUvRGVza3RvcC9DRVJOL3RyYWluXzEwMF9ldmVudHMvZXZlbnQwMDAwMDEwMDAtaGl0cy5jc3YnKQpwYXJ0aWNsZXMgPSByZWFkLmNzdignL1VzZXJzL0pvbm5pZS9EZXNrdG9wL0NFUk4vdHJhaW5fMTAwX2V2ZW50cy9ldmVudDAwMDAwMTAwMC1wYXJ0aWNsZXMuY3N2JykKdHJ1dGggPSByZWFkLmNzdignL1VzZXJzL0pvbm5pZS9EZXNrdG9wL0NFUk4vdHJhaW5fMTAwX2V2ZW50cy9ldmVudDAwMDAwMTAwMC10cnV0aC5jc3YnKQpgYGAKCgojIyMgQ2hhbmdlIHR5cGVzCmBgYHtyfQpmb3IgKGkgaW4gMTpsZW5ndGgoY2VsbHMpKXsKICBwcmludChwYXN0ZSgnY2VsbHM6JyxuYW1lcyhjZWxscylbaV0sJzonLGNsYXNzKGNlbGxzWyxpXSkpKQp9CmZvciAoaSBpbiAxOmxlbmd0aChoaXRzKSl7CiAgcHJpbnQocGFzdGUoJ2hpdHM6JywgbmFtZXMoaGl0cylbaV0sJzonLGNsYXNzKGhpdHNbLGldKSkpCn0KZm9yIChpIGluIDE6bGVuZ3RoKHBhcnRpY2xlcykpewogIHByaW50KHBhc3RlKCdwYXJ0aWNsZXM6JywgbmFtZXMocGFydGljbGVzKVtpXSwnOicsY2xhc3MocGFydGljbGVzWyxpXSkpKQp9CmZvciAoaSBpbiAxOmxlbmd0aCh0cnV0aCkpewogIHByaW50KHBhc3RlKCd0cnV0aDonLCBuYW1lcyh0cnV0aClbaV0sJzonLGNsYXNzKHRydXRoWyxpXSkpKQp9CmBgYAoKYGBge3J9CmNlbGxzJGhpdF9pZCA9IGZhY3RvcihjZWxscyRoaXRfaWQpCmNlbGxzJGNoMCA9IGFzLm51bWVyaWMoY2VsbHMkY2gwKQpjZWxscyRjaDEgPSBhcy5udW1lcmljKGNlbGxzJGNoMSkKaGl0cyRoaXRfaWQgPSBmYWN0b3IoaGl0cyRoaXRfaWQpCmhpdHMkdm9sdW1lX2lkID0gZmFjdG9yKGhpdHMkdm9sdW1lX2lkKQpoaXRzJGxheWVyX2lkID0gZmFjdG9yKGhpdHMkbGF5ZXJfaWQpCmhpdHMkbW9kdWxlX2lkID0gZmFjdG9yKGhpdHMkbW9kdWxlX2lkKQpwYXJ0aWNsZXMkcGFydGljbGVfaWQgPSBmYWN0b3IocGFydGljbGVzJHBhcnRpY2xlX2lkKQp0cnV0aCRwYXJ0aWNsZV9pZCA9IGZhY3Rvcih0cnV0aCRwYXJ0aWNsZV9pZCkKCgpgYGAKCiMjIyBQYXJ0aWNsZXMgKyB0cnV0aAoKYGBge3J9CnBhcnRpY2xlc190cnV0aCA8LSBtZXJnZShwYXJ0aWNsZXMsdHJ1dGgsYnk9InBhcnRpY2xlX2lkIikKYGBgCgoKCgoKCmBgYHtyfQpwYXJ0aWNsZXNfdHJ1dGgkbmV0X3ggPSBhYnMocGFydGljbGVzX3RydXRoJHZ4IC0gcGFydGljbGVzX3RydXRoJHR4KQpwYXJ0aWNsZXNfdHJ1dGgkbmV0X3kgPSBhYnMocGFydGljbGVzX3RydXRoJHZ5IC0gcGFydGljbGVzX3RydXRoJHR5KQpwYXJ0aWNsZXNfdHJ1dGgkbmV0X3ogPSBhYnMocGFydGljbGVzX3RydXRoJHZ6IC0gcGFydGljbGVzX3RydXRoJHR6KQpwYXJ0aWNsZXNfdHJ1dGgkdHdvX25vcm0gPSBzcXJ0KHBhcnRpY2xlc190cnV0aCRuZXRfeF4yICsgcGFydGljbGVzX3RydXRoJG5ldF95XjIgKyBwYXJ0aWNsZXNfdHJ1dGgkbmV0X3peMikKYGBgCgojIyMgb3JnaW5hbCBhbmQgbGFzdCBwb3NpdGlvbgoKIyMjIG5ldCBkaXNwbGFjZW1lbnQKYGBge3J9CmRmID0gcGFydGljbGVzX3RydXRoWzEsXQpkZiA9IGRmWy0xLF0KaSA9IDEKd2hpbGUgKGkgPCBucm93KHBhcnRpY2xlc190cnV0aCkpewogIGtleSA9IHBhcnRpY2xlc190cnV0aCRwYXJ0aWNsZV9pZFtpXQogIHRlc3QgPSBwYXJ0aWNsZXNfdHJ1dGhbcGFydGljbGVzX3RydXRoJHBhcnRpY2xlX2lkID09IGtleSxdCiAgZGYgPSByYmluZChkZix0ZXN0W25yb3codGVzdCksXSkKICBpID0gaSArIG5yb3codGVzdCkKICBpZihucm93KGRmKSA+IDUwMCl7CiAgICBicmVhawogIH0KfQpgYGAKCmBgYHtyfQpkZiRxID0gZmFjdG9yKGRmJHEpCmNsYXNzKGRmJHEpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0ZXgyZXhwKQpkZl9wbG90ID0gcXBsb3QoZGYkdHdvX25vcm0sYmlucyA9MTUgKQpkZl9wbG90KyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBUb3RhbCBkaXNwbGFjZW1lbnQiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAreGxhYihUZVgoIiR8fCRsYXN0IHBvc2l0aW9uIC0gb3JpZ2luIHBvc2l0b24kfHxfMiQiKSkgCmBgYAoKCgoKU3BsaXQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGNoYXJnZSBkYXRhCmBgYHtyfQpwb3NfY2hhcmdlID0gZGZbZGYkcSA9PSAxLF0KbmVnX2NoYXJnZSA9IGRmW2RmJHEgPT0tMSxdCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBwb3NfY2hhcmdlLCBhZXMoeCA9IHR3b19ub3JtLGNvbG9yID0gJ2JsdWUnKSxiaW5zID0gMTUpICsgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IG5lZ19jaGFyZ2UsIGFlcyh4ID0gdHdvX25vcm0sY29sb3IgPSAncmVkJyksYmlucyA9IDE1KSArIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFRvdGFsIGRpc3BsYWNlbWVudDogUG9zaXRpdmUgdnMgTmVnYXRpdmVseSBjaGFyZ2VkIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoVGVYKCIkfHwkbGFzdCBwb3NpdGlvbiAtIG9yaWdpbiBwb3NpdG9uJHx8XzIkIikpCmBgYAoKClJ1biB0ZXN0IApgYGB7cn0KbWVhbihuZWdfY2hhcmdlJHR3b19ub3JtKQptZWFuKHBvc19jaGFyZ2UkdHdvX25vcm0pCndpbGNveC50ZXN0KG5lZ19jaGFyZ2UkdHdvX25vcm0sIHBvc19jaGFyZ2UkdHdvX25vcm0pCmBgYAoKSW5kZWVkIGRpc3RyaWJ1dGlvbnMgYXJlIHNpbWlsYXIgd2hlbiBub3JtYWxpemVkIApgYGB7cn0KZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gcG9zX2NoYXJnZSwgYWVzKHggPSB0d29fbm9ybSx5PS4uY291bnQuLi9zdW0oLi5jb3VudC4uKSxjb2xvciA9ICdibHVlJyksYmlucyA9IDE1KSArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBuZWdfY2hhcmdlLCBhZXMoeCA9IHR3b19ub3JtLHk9Li5jb3VudC4uL3N1bSguLmNvdW50Li4pLGNvbG9yID0gJ3JlZCcpLGJpbnMgPSAxNSkgKyBsYWJzKHRpdGxlID0gIk5vcm1hbGl6ZWQgRGlzdHJpYnV0aW9uOiBQb3NpdGl2ZSB2cyBOZWdhdGl2ZWx5IGNoYXJnZWQiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAreGxhYihUZVgoIiR8fCRsYXN0IHBvc2l0aW9uIC0gb3JpZ2luIHBvc2l0b24kfHxfMiQiKSkKYGBgCgoKCgoKUnVuIGNoaS1zcXVhcmUgdG8gc2VlIGlmIHR3b19ub3JtIGRhdGEgZm9sbG93cyBleHBvZW5lbnRpYWwgLSBGYWlsCmBgYHtyfQpkZl90ZXN0ID0gbGF5ZXJfZGF0YShkZl9wbG90LDEpIApwMiA9IHBleHAoZGZfdGVzdCR4bWF4LHJhdGUgPSAxL21lYW4oZGYkdHdvX25vcm0pKSAtIHBleHAocG1heChkZl90ZXN0JHhtaW4sMCkscmF0ZSA9IDEvbWVhbihkZiR0d29fbm9ybSkpCmBgYAoKYGBge3J9CmNoaXNxLnRlc3QoZGZfdGVzdCRjb3VudCwgcCA9IHAyLCByZXNjYWxlLnAgPSBUUlVFKQpgYGAKCiMjIyBmaXJzdCBkaXNwbGFjZW1lbnQKYGBge3J9CmRmID0gcGFydGljbGVzX3RydXRoWzEsXQpkZiA9IGRmWy0xLF0KaSA9IDEKd2hpbGUgKGkgPCBucm93KHBhcnRpY2xlc190cnV0aCkpewogIGtleSA9IHBhcnRpY2xlc190cnV0aCRwYXJ0aWNsZV9pZFtpXQogIHRlc3QgPSBwYXJ0aWNsZXNfdHJ1dGhbcGFydGljbGVzX3RydXRoJHBhcnRpY2xlX2lkID09IGtleSxdCiAgZGYgPSByYmluZChkZiwgdGVzdFsxLF0pCiAgaSA9IGkgKyBucm93KHRlc3QpCiAgaWYobnJvdyhkZikgPiA1MDApewogICAgYnJlYWsKICB9Cn0KYGBgCgoKYGBge3J9CmRmJHEgPSBmYWN0b3IoZGYkcSkKY2xhc3MoZGYkcSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZGZfcGxvdCA9IHFwbG90KGRmJHR3b19ub3JtLGJpbnMgPTE1ICkKZGZfcGxvdCArIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZpcnN0IGRpc3BsYWNlbWVudCIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICt4bGFiKFRlWCgiJHx8JGZpcnN0IGhpdCBwb3NpdGlvbiAtIG9yaWdpbiBwb3NpdG9uJHx8XzIkIikpCmBgYAoKU3BsaXQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGNoYXJnZSBkYXRhCmBgYHtyfQpwb3NfY2hhcmdlID0gZGZbZGYkcSA9PSAxLF0KbmVnX2NoYXJnZSA9IGRmW2RmJHEgPT0tMSxdCm5yb3cocG9zX2NoYXJnZSkKbnJvdyhuZWdfY2hhcmdlKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBwb3NfY2hhcmdlLCBhZXMoeCA9IHR3b19ub3JtLGNvbG9yID0gJ2JsdWUnKSxiaW5zID0gMTUpICsgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IG5lZ19jaGFyZ2UsIGFlcyh4ID0gdHdvX25vcm0sY29sb3IgPSAncmVkJyksYmlucyA9IDE1KSAgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBmaXJzdCBkaXNwbGFjZW1lbnQ6IFBvc2l0aXZlIHZzIE5lZ2F0aXZlbHkgY2hhcmdlZCIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICt4bGFiKFRlWCgiJHx8JGZpcnN0IGhpdCBwb3NpdGlvbiAtIG9yaWdpbiBwb3NpdG9uJHx8XzIkIikpCmBgYAoKCgpSdW4gdGVzdCAKYGBge3J9Cm1lYW4obmVnX2NoYXJnZSR0d29fbm9ybSkKbWVhbihwb3NfY2hhcmdlJHR3b19ub3JtKQp3aWxjb3gudGVzdChuZWdfY2hhcmdlJHR3b19ub3JtLCBwb3NfY2hhcmdlJHR3b19ub3JtKQpgYGAKTm9ybWFsaXplZApgYGB7cn0KZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gcG9zX2NoYXJnZSwgYWVzKHggPSB0d29fbm9ybSx5PS4uY291bnQuLi9zdW0oLi5jb3VudC4uKSxjb2xvciA9ICdibHVlJyksYmlucyA9IDE1KSArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBuZWdfY2hhcmdlLCBhZXMoeCA9IHR3b19ub3JtLHk9Li5jb3VudC4uL3N1bSguLmNvdW50Li4pLGNvbG9yID0gJ3JlZCcpLGJpbnMgPSAxNSkgICsgbGFicyh0aXRsZSA9ICJOb3JtYWxpemVkIERpc3RyaWJ1dGlvbjogUG9zaXRpdmUgdnMgTmVnYXRpdmVseSBjaGFyZ2VkIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoVGVYKCIkfHwkZmlyc3QgaGl0IHBvc2l0aW9uIC0gb3JpZ2luIHBvc2l0b24kfHxfMiQiKSkKYGBgCgoKYGBge3J9CmRmX3Rlc3QgPSBsYXllcl9kYXRhKGRmX3Bsb3QsMSkgCnAyID0gcGV4cChkZl90ZXN0JHhtYXgscmF0ZSA9IDEvbWVhbihkZiR0d29fbm9ybSkpIC0gcGV4cChwbWF4KGRmX3Rlc3QkeG1pbiwwKSxyYXRlID0gMS9tZWFuKGRmJHR3b19ub3JtKSkKYGBgCgpgYGB7cn0KY2hpc3EudGVzdChkZl90ZXN0JGNvdW50LCBwID0gcDIsIHJlc2NhbGUucCA9IFRSVUUpCmBgYAo+CiBTYW1lIHJlc3VsdHMgZm9yIEZpcnN0IGRpc3BsYWNlbWVudAoKCgojIyMgVW5pcXVlIHZhbHVlcyBpbiBlYWNoIGV2ZW50CmBgYHtyfQpudW1iZXJfcGFydGljbGVzID0gYygpCmZvciggaSBpbiAwOjk5KXsKICBldmVudCA9IDEwMDAgKyBpCiAgZXZlbnRfc3RyaW5nID0gdG9TdHJpbmcoZXZlbnQpCiAgbGluayA9IHBhc3RlMCgnL1VzZXJzL0pvbm5pZS9EZXNrdG9wL0NFUk4vdHJhaW5fMTAwX2V2ZW50cy9ldmVudDAwMDAwJyxldmVudF9zdHJpbmcsJy1wYXJ0aWNsZXMuY3N2JykKICBwYXJ0aWNsZXMyID0gcmVhZC5jc3YobGluaykKICBudW1iZXJfcGFydGljbGVzID0gYyhudW1iZXJfcGFydGljbGVzLG5yb3cocGFydGljbGVzMikpCn0KYGBgCgoKCmBgYHtyfQpudW1iZXJfaW5fZXZlbnQgPSBxcGxvdChudW1iZXJfcGFydGljbGVzLGJpbnMgPSAxMCkKbnVtYmVyX2luX2V2ZW50ICArIGxhYnModGl0bGUgPSAiTnVtYmVyIG9mIHBhcnRpY2xlcyBhY3Jvc3MgZXZlbnRzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoVGVYKCJOdW1iZXIgb2YgcGFydGljbGVzIikpCmBgYAojIyMgQ2hpLXNxdWFyZQpgYGB7cn0KZXZlbnRfdGVzdCA9IGxheWVyX2RhdGEobnVtYmVyX2luX2V2ZW50LDEpIApwMSA9IHBub3JtKGV2ZW50X3Rlc3QkeG1heCxtZWFuID0gbWVhbihudW1iZXJfcGFydGljbGVzKSxzZCA9IHNkKG51bWJlcl9wYXJ0aWNsZXMpKSAtIHBub3JtKGV2ZW50X3Rlc3QkeG1pbixtZWFuID0gbWVhbihudW1iZXJfcGFydGljbGVzKSwgc2QgPSBzZChudW1iZXJfcGFydGljbGVzKSkKYGBgCgpgYGB7cn0KY2hpc3EudGVzdChldmVudF90ZXN0JGNvdW50LCBwID0gcDEsIHJlc2NhbGUucCA9IFRSVUUpCmBgYAoKCgojIyBLUy10ZXN0CmBgYHtyfQprcy50ZXN0KG51bWJlcl9wYXJ0aWNsZXMsInBub3JtIixtZWFuKG51bWJlcl9wYXJ0aWNsZXMpLHNkKG51bWJlcl9wYXJ0aWNsZXMpKQpgYGAKIElUUyBOT1JNQUwKCgojIyMgUGFydGljbGVzIHdpdGggbm8gaGl0cwpDaGVjayBudW1iZXJzIG9mIHBhcnRpY2xlIGlkcyB3aXRoIG5vIGRhdGEgZm9yIHRydXRoCmBgYHtyfQpsZW5ndGgodW5pcXVlKHBhcnRpY2xlcyRwYXJ0aWNsZV9pZCkpCmxlbmd0aCh1bmlxdWUocGFydGljbGVzX3RydXRoJHBhcnRpY2xlX2lkKSkKbGVuZ3RoKHNldGRpZmYocGFydGljbGVzJHBhcnRpY2xlX2lkLHRydXRoJHBhcnRpY2xlX2lkKSkKYGBgCgpgYGB7cn0Kbm9fdHJ1dGggPSBwYXJ0aWNsZXNbcGFydGljbGVzJHBhcnRpY2xlX2lkICVpbiUgc2V0ZGlmZihwYXJ0aWNsZXMkcGFydGljbGVfaWQsdHJ1dGgkcGFydGljbGVfaWQpLF0KbnJvdyhub190cnV0aCkKYGBgCgoKCgojIyMgVGVzdCBwcm9wb3J0aW9uIG9mIGNoYXJnZSBpbiBwYXJ0aWNsZXMgd2l0aCBubyBoaXRzCmBgYHtyfQpxcGxvdChwYXJ0aWNsZXNfdHJ1dGgkcSxiaW5zID0gMTApICArIGxhYnModGl0bGUgPSAiQ2hhcmdlIG9mIHBhcnRpY2xlcyBmb3IgdGhvc2Ugd2l0aCBoaXRzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoIkNoYXJnZSBvZiBwYXJ0aWNsZSIpCnFwbG90KG5vX3RydXRoJHEsYmlucyA9IDEwKSsgbGFicyh0aXRsZSA9ICJDaGFyZ2Ugb2YgcGFydGljbGVzIGZvciB0aG9zZSB3aXRoIG5vIGhpdHMiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAreGxhYigiQ2hhcmdlIG9mIHBhcnRpY2xlIikKbm9fdHJ1dGhfcHJvcCA9IG5yb3cobm9fdHJ1dGhbbm9fdHJ1dGgkcSA9PSAxLF0pL2xlbmd0aChub190cnV0aCRxKQp5ZXNfdHJ1dGhfcHJvcCA9IG5yb3cocGFydGljbGVzX3RydXRoW3BhcnRpY2xlc190cnV0aCRxID09IDEsXSkvbGVuZ3RoKHBhcnRpY2xlc190cnV0aCRxKQpwYXN0ZTAoJ25vX3RydXRoX3Byb3A6ICcsbm9fdHJ1dGhfcHJvcCkKcGFzdGUwKCd5ZXNfdHJ1dGhfcHJvcDogJyx5ZXNfdHJ1dGhfcHJvcCkKCmBgYApaLSBUZXN0IG9mIHByb3BvcnRpb25zCmBgYHtyfQpwX2UgPSBucm93KHBhcnRpY2xlc1twYXJ0aWNsZXMkcSA9PSAxLF0pL2xlbmd0aChwYXJ0aWNsZXMkcSkKel9vYnNlcnZlZCA9IChub190cnV0aF9wcm9wIC0geWVzX3RydXRoX3Byb3ApLyhzcXJ0KChsZW5ndGgocGFydGljbGVzJHEpKnBfZSooMS1wX2UpKS8obGVuZ3RoKG5vX3RydXRoJHEpKmxlbmd0aChwYXJ0aWNsZXNfdHJ1dGgkcSkpKSkKcGFzdGUwKCdaIHNjb3JlOiAnLHpfb2JzZXJ2ZWQpCnBhc3RlMCgncC12YWx1ZTogJywxLXBub3JtKHpfb2JzZXJ2ZWQpKQpgYGAKCj4gCkFIQSEgUHJvcG9ydGlvbnMgYXJlIGRpZmZlcmVudCEgVGhlcmUgaXMgbW9yZSBwb3NpdGl2ZWx5IGNoYXJnZWQgcGFydGljbGVzIChoaWdoZXIgcHJvcG9ydGlvbiBvZiBwb3NpdGl2ZWx5IGNoYXJnZWQgcGFydGljbGVzKSBpbiB0aGUgc2V0IHdoZXJlIHRoZXJlIGFyZSBubyBoaXRzIQoKCgpWaXN1YWxpemluZyBzdGFydGluZyBwb3NpdGlvbnMgb2YgcGFydGljbGVzIHdpdGggMCBoaXRzCmBgYHtyfQpsaWJyYXJ5KHBsb3RseSkKcGFja2FnZVZlcnNpb24oJ3Bsb3RseScpCgpwbG90X2x5KG5vX3RydXRoLHggPSAgfnZ4LHkgPSB+dnkseiA9IH52eixjb2xvciA9IH5xLGNvbG9ycyA9YygnYmx1ZScsJ3JlZCcpICkKYGBgCgoKCiMjIyBNb21lbnR1bSBvZiBwYXJ0aWNsZXMgd2l0aCBubyBoaXRzCmBgYHtyfQpub190cnV0aCRNb21fbm9ybSA9IHNxcnQobm9fdHJ1dGgkcHheMiArIG5vX3RydXRoJHB5XjIgKyBub190cnV0aCRwel4yKQpwYXJ0aWNsZXNfdHJ1dGgkTW9tX25vcm0gPSBzcXJ0KHBhcnRpY2xlc190cnV0aCRweF4yICsgcGFydGljbGVzX3RydXRoJHB5XjIgKyBwYXJ0aWNsZXNfdHJ1dGgkcHpeMikKYGBgCgpgYGB7cn0KaGVhZChub190cnV0aCkKYGBgCgoKYGBge3J9CnFwbG90KG5vX3RydXRoJE1vbV9ub3JtLGJpbnMgPSAxMCkgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBtb21lbnR1bSBmb3Igbm8gaGl0cyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICt4bGFiKFRlWCgiJHx8JE1vbWVudHVtJHx8XzIkIikpCnFwbG90KHBhcnRpY2xlc190cnV0aCRNb21fbm9ybSxiaW5zID0gMTApICArIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIG1vbWVudHVtIGZvciBwYXJ0aWNsZXMgd2l0aCBoaXRzIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoVGVYKCIkfHwkTW9tZW50dW0kfHxfMiQiKSkKcGFzdGUwKCdub190cnV0aDogJyxtZWFuKG5vX3RydXRoJE1vbV9ub3JtKSkgCnBhc3RlMCgncGFydGljbGVzX3RydXRoOiAnLG1lYW4ocGFydGljbGVzX3RydXRoJE1vbV9ub3JtKSkgCmBgYAo+IApNb21lbnR1bSBub3JtIG9mIHBhcnRpY2xlcyBzZWVtIHRvIGJlIGFyb3VuZCAwIHJlZ2FyZGxlc3Mgb2YgaGF2aW5nIGFueSBoaXRzIG9yIG5vdAoKClJ1biB0ZXN0IApgYGB7cn0Kd2lsY294LnRlc3Qobm9fdHJ1dGgkTW9tX25vcm0sIHBhcnRpY2xlc190cnV0aCRNb21fbm9ybSkKYGBgCgoKCiMjIyBNb21lbnR1bSBwcmVkaWN0aW5nIG5oaXRzCgoKYGBge3J9Cm1vbV9wcmVkX25oaXRzID0gbG0oZGF0YSA9IHBhcnRpY2xlcywgbmhpdHN+TW9tX25vcm0pCnN1bW1hcnkobW9tX3ByZWRfbmhpdHMpCnRlbXBfdmFyIDwtIHByZWRpY3QobW9tX3ByZWRfbmhpdHMsIGludGVydmFsPSJwcmVkaWN0aW9uIikKbmV3X2RmIDwtIGNiaW5kKHBhcnRpY2xlcywgdGVtcF92YXIpCmJvYiA9IGdncGxvdChkYXRhID0gbmV3X2RmICwgYWVzKHg9TW9tX25vcm0sIHk9bmhpdHMpKSArCiAgZ2VvbV9wb2ludCgpICsgICAgCiAgZ2VvbV9saW5lKGFlcyh5PWx3ciksIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKwogIGdlb21fbGluZShhZXMoeT11cHIpLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sc2UgPSBUUlVFKSAgICArIGxhYnModGl0bGUgPSAiTW9tZW50dW0gcHJlZGljdCBuaGl0cyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICt4bGFiKFRlWCgiJHx8JE1vbWVudHVtJHx8XzIkIikpCmJvYgpgYGAKPgpNb21lbnR1bSBob3JyZW5kb3VzIGF0IHByZWRpY3RpbmcgbmhpdHMgc2luY2UgaXQgY2x1c3RlcnMgYXQgMCByZWdhcmRsZXNzIG9mIG5oaXRzCgoKCgoKWm9vbSBpbiBwZXJoYXBzPwoKYGBge3J9CnBhcnRpY2xlc19tTm9ybV9sZXNzNTA9IHBhcnRpY2xlc1twYXJ0aWNsZXMkTW9tX25vcm0gPCA1MCxdCm1vbV9wcmVkX25oaXRzID0gbG0oZGF0YSA9IHBhcnRpY2xlc19tTm9ybV9sZXNzNTAsIG5oaXRzfk1vbV9ub3JtKQpzdW1tYXJ5KG1vbV9wcmVkX25oaXRzKQp0ZW1wX3ZhciA8LSBwcmVkaWN0KG1vbV9wcmVkX25oaXRzLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCm5ld19kZiA8LSBjYmluZChwYXJ0aWNsZXNfbU5vcm1fbGVzczUwLCB0ZW1wX3ZhcikKYm9iID0gZ2dwbG90KGRhdGEgPSBuZXdfZGYgLCBhZXMoeD1Nb21fbm9ybSwgeT1uaGl0cykpICsKICBnZW9tX3BvaW50KCkgKyAgICAKICBnZW9tX2xpbmUoYWVzKHk9bHdyKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikrCiAgZ2VvbV9saW5lKGFlcyh5PXVwciksIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxzZSA9IFRSVUUpICAgICsgbGFicyh0aXRsZSA9ICJNb21lbnR1bSBwcmVkaWN0IG5oaXRzOiBab29tIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgK3hsYWIoVGVYKCIkfHwkTW9tZW50dW0kfHxfMiQiKSkKYm9iCmBgYAoKIyMgRmluZCBtb2RlcwpgYGB7cn0KdGVzdCA9IGhpc3QocGFydGljbGVzJG5oaXRzLGJyZWFrcyA9IDIwKQptb2RlMSA9IHRlc3QkbWlkc1t0ZXN0JGNvdW50cyA9PSBtYXgodGVzdCRjb3VudHMpXQptb2RlMiA9IHRlc3QkbWlkc1t0ZXN0JGNvdW50cyA9PSBzb3J0KHRlc3QkY291bnRzLHBhcnRpYWwgPSBsZW5ndGgodGVzdCRjb3VudHMpIC0gMSlbbGVuZ3RoKHRlc3QkY291bnRzKS0xXV0KbWlkcG9pbnQgPSAobW9kZTEgKyBtb2RlMikvMgpgYGAKSWdub3JlIHBsb3QgYWJvdmUKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBhcnRpY2xlcyAsIGFlcyh4PW5oaXRzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICsgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCAgPSBtaWRwb2ludCxjb2xvciA9ICdtaWRwb2ludCcpKSArIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtb2RlMSxjb2xvciA9ICdtb2RlMScpKSArIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtb2RlMixjb2xvciA9ICdtb2RlMicpICApICsgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgbmhpdHMiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkxhYmVscyIsIHZhbHVlcyA9IGMobWlkcG9pbnQgPSAiYmx1ZSIsIG1vZGUxID0gJ3JlZCcsIG1vZGUyID0gImdyZWVuIikpCgoKCmBgYAoKIyMgQ2hvcCBlbSAKYGBge3J9CmV4cG9fc2lkZSA9IHBhcnRpY2xlc1twYXJ0aWNsZXMkbmhpdHMgPCBtaWRwb2ludCxdJG5oaXRzCm5vcm1fc2lkZSA9IHBhcnRpY2xlc1twYXJ0aWNsZXMkbmhpdHMgPiBtaWRwb2ludCxdJG5oaXRzCmV4cG9fcGxvdCA9IHFwbG90KGV4cG9fc2lkZSxiaW5zPSA3KQpub3JtX3Bsb3QgPSBxcGxvdChub3JtX3NpZGUsYmlucz03KQpleHBvX3Bsb3QgKyBsYWJzKHRpdGxlID0gIkV4cG9uZW50aWFsIHNpZGUiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSt4bGFiKCduaGl0cycpCm5vcm1fcGxvdCsgbGFicyh0aXRsZSA9ICJOb3JtYWwgc2lkZSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK3hsYWIoJ25oaXRzJykKYGBgCgoKCgpgYGB7cn0Kbm9ybV90ZXN0ID0gbGF5ZXJfZGF0YShub3JtX3Bsb3QsMSkgCnAxID0gcG5vcm0obm9ybV90ZXN0JHhtYXgsbWVhbiA9IG1lYW4obm9ybV9zaWRlKSxzZCA9IHNkKG5vcm1fc2lkZSkpIC0gcG5vcm0obm9ybV90ZXN0JHhtaW4sbWVhbiA9IG1lYW4obm9ybV9zaWRlKSwgc2QgPSBzZChub3JtX3NpZGUpKQpgYGAKCmBgYHtyfQpjaGlzcS50ZXN0KG5vcm1fdGVzdCRjb3VudCwgcCA9IHAxLCByZXNjYWxlLnAgPSBUUlVFKQpgYGAKCgpgYGB7cn0KZXhwb190ZXN0ID0gbGF5ZXJfZGF0YShleHBvX3Bsb3QsMSkgCnAyID0gcGV4cChleHBvX3Rlc3QkeG1heCxyYXRlID0gMS9tZWFuKGV4cG9fc2lkZSkpIC0gcGV4cChwbWF4KGV4cG9fdGVzdCR4bWluLDApLHJhdGUgPSAxL21lYW4oZXhwb19zaWRlKSkKYGBgCgpgYGB7cn0KY2hpc3EudGVzdChleHBvX3Rlc3QkY291bnQsIHAgPSBwMiwgcmVzY2FsZS5wID0gVFJVRSkKYGBgCgojIyBLUyAgdGVzdApgYGB7cn0KZXggPSByZXhwKDEwMDAwLCByYXRlID0gMS9tZWFuKGV4cG9fc2lkZSkpCmtzLnRlc3QoZXgsInBleHAiLGV4cG9fc2lkZSkKYGBgCgo+CkZBSUxVUkUgQUdBSU4KCgojIyBLdXJ0b3NpcyBUZXN0CmBgYHtyfQpsaWJyYXJ5KCdtb21lbnRzJykKa3VydG9zaXMoZXhwb19zaWRlKQpleF9zZXQgPSBjKCkKZm9yIChpIGluIDE6MTAwMCl7CiAgZXggPSByZXhwKDEwMDAsIHJhdGUgPSAxL21lYW4oZXhwb19zaWRlKSkKICBleF9rID0ga3VydG9zaXMoZXgpCiAgZXhfc2V0ID0gYyhleF9zZXQsZXhfaykKfQpxcGxvdChleF9zZXQsYmlucyA9IDEwKSArIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBrdXJ0b3NpcyhleHBvX3NpZGUpLGNvbG9yID0gJ3NhbXBsZV92YWx1ZScpKSArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkxhYmVscyIsIHZhbHVlcyA9IGMoc2FtcGxlX3ZhbHVlID0gImJsdWUiKSkgKyBsYWJzKHRpdGxlID0gIkt1cnRvc2lzIERpc3RyaWJ1dGlvbiBvZiBleHBvbmVudGlhbCB3aXRoIHJhdGUgb2Ygc2FtcGxlIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkreGxhYignS3VydG9zaXMnKQpgYGAKCgpgYGB7cn0Ka3VydG9zaXMobm9ybV9zaWRlKQpubV9zZXQgPSBjKCkKZm9yIChpIGluIDE6MTAwMCl7CiAgbm0gPSBybm9ybSgxMDAwMCwgbWVhbiA9IG1lYW4obm9ybV9zaWRlKSwgc2QgPSBzZChub3JtX3NpZGUpKQogIG5tX2sgPSBrdXJ0b3NpcyhubSkKICBubV9zZXQgPSBjKG5tX3NldCxubV9rKQp9CnFwbG90KG5tX3NldCxiaW5zID0gMTApICsgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IGt1cnRvc2lzKG5vcm1fc2lkZSksY29sb3IgPSAnc2FtcGxlX3ZhbHVlJykpICArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkxhYmVscyIsIHZhbHVlcyA9IGMoc2FtcGxlX3ZhbHVlID0gImJsdWUiKSkgKyBsYWJzKHRpdGxlID0gIkt1cnRvc2lzIERpc3RyaWJ1dGlvbiBvZiBOb3JtYWwgd2l0aCBwYXJhbWV0ZXJzIG9mIHNhbXBsZSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK3hsYWIoJ0t1cnRvc2lzJykKYGBgCgoKIyMjIFN1bSBvZiB2YWx1ZXMgYnkgcGFydGljbGVfaWQKCmBgYHtyfQpwYXJ0aWNsZXNfdmFsdWUgPC0gbWVyZ2UocGFydGljbGVzX3RydXRoLGNlbGxzLGJ5PSJoaXRfaWQiKQpwYXJ0aWNsZXNfdmFsdWUgPSBhZ2dyZWdhdGUodmFsdWUgfiBoaXRfaWQgKyBwYXJ0aWNsZV9pZCtxLGRhdGEgPSBwYXJ0aWNsZXNfdmFsdWUsRlVOID0gc3VtKQpwYXJ0aWNsZXNfdmFsdWUgPSBhZ2dyZWdhdGUodmFsdWUgfiBwYXJ0aWNsZV9pZCtxLGRhdGEgPSBwYXJ0aWNsZXNfdmFsdWUsRlVOID0gc3VtKQpgYGAKCgpgYGB7cn0KdmFsdWVfcGxvdCA9IHFwbG90KHBhcnRpY2xlc192YWx1ZSR2YWx1ZSxiaW5zID0gMTApCnZhbHVlX3Bsb3QgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBzdW0gb2YgcGFydGljbGUgdmFsdWVzIGJ5IElEIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkreGxhYignUGFydGljbGUgdmFsdWVzJykKYGBgCgojIyBDaGktc3F1YXJlIHRlc3QKYGBge3J9CnZhbHVlX3Rlc3QgPSBsYXllcl9kYXRhKHZhbHVlX3Bsb3QsMSkgCnAyID0gcGV4cCh2YWx1ZV90ZXN0JHhtYXgscmF0ZSA9IDEvbWVhbihwYXJ0aWNsZXNfdmFsdWUkdmFsdWUpKSAtIHBleHAocG1heCh2YWx1ZV90ZXN0JHhtaW4sMCkscmF0ZSA9IDEvbWVhbihwYXJ0aWNsZXNfdmFsdWUkdmFsdWUpKQpgYGAKCmBgYHtyfQpjaGlzcS50ZXN0KHZhbHVlX3Rlc3QkY291bnQsIHAgPSBwMiwgcmVzY2FsZS5wID0gVFJVRSkKYGBgCgoKIyMgS1MgIHRlc3QKYGBge3J9CmV4ID0gcmV4cCgxMDAwMCwgcmF0ZSA9IDEvbWVhbihwYXJ0aWNsZXNfdmFsdWUkdmFsdWUpKQprcy50ZXN0KGV4LCJwZXhwIixwYXJ0aWNsZXNfdmFsdWUkdmFsdWUpCmBgYAoKCmBgYHtyfQpuZWdfdmFsdWUgPSBwYXJ0aWNsZXNfdmFsdWVbcGFydGljbGVzX3ZhbHVlJHEgPT0gLTEsXQpwb3NfdmFsdWUgPSBwYXJ0aWNsZXNfdmFsdWVbcGFydGljbGVzX3ZhbHVlJHEgPT0xLF0KYGBgCgoKYGBge3J9CnFwbG90KG5lZ192YWx1ZSR2YWx1ZSxiaW5zID0gMTUpKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBmb3IgbmVnYXRpdmUgY2hhcmdlIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkreGxhYignUGFydGljbGUgdmFsdWUnKQpxcGxvdChwb3NfdmFsdWUkdmFsdWUsYmlucyA9IDE1KSsgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gZm9yIHBvc2l0aXZlIGNoYXJnZSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpK3hsYWIoJ1BhcnRpY2xlIHZhbHVlJykKYGBgCgoKVGVzdCBpZiBtZWFucyBhcmUgZGlmZgpgYGB7cn0KbWVhbihuZWdfdmFsdWUkdmFsdWUpCm1lYW4ocG9zX3ZhbHVlJHZhbHVlKQp3aWxjb3gudGVzdChuZWdfdmFsdWUkdmFsdWUsIHBvc192YWx1ZSR2YWx1ZSkKYGBgCgpgYGB7cn0KZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShkYXRhID0gcG9zX3ZhbHVlLCBhZXMoeCA9IHZhbHVlLGNvbG9yID0gJ2JsdWUnKSxiaW5zID0gMTUpICsgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IG5lZ192YWx1ZSwgYWVzKHggPSB2YWx1ZSxjb2xvciA9ICdyZWQnKSxiaW5zID0gMTUpKyBsYWJzKHRpdGxlID0gIlBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBzaWRlIGJ5IHNpZGUiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSt4bGFiKCdQYXJ0aWNsZSB2YWx1ZScpCmBgYAoKCiMjIyBzdW0gb2YgYWxsIHZhbHVlcyBpbiBhbGwgZXZlbnRzCmBgYHtyfQpudW1iZXJfdmFsdWVzID0gYygpCmZvciggaSBpbiAwOjk5KXsKICBldmVudCA9IDEwMDAgKyBpCiAgZXZlbnRfc3RyaW5nID0gdG9TdHJpbmcoZXZlbnQpCiAgbGluayA9IHBhc3RlMCgnL1VzZXJzL0pvbm5pZS9EZXNrdG9wL0NFUk4vdHJhaW5fMTAwX2V2ZW50cy9ldmVudDAwMDAwJyxldmVudF9zdHJpbmcsJy1jZWxscy5jc3YnKQogIGNlbGxzMiA9IHJlYWQuY3N2KGxpbmspCiAgbnVtYmVyX3ZhbHVlcyA9IGMobnVtYmVyX3ZhbHVlcyxzdW0oY2VsbHMyJHZhbHVlKSkKfQpgYGAKCmBgYHtyfQp2YWx1ZXNfcGxvdCA9IHFwbG90KG51bWJlcl92YWx1ZXMsYmlucyA9IDE1KQp2YWx1ZXNfcGxvdCsgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2Ygc3VtIG9mIGFsbCB2YWx1ZXMgcGVyIGV2ZW50IikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkreGxhYignVmFsdWVzIHBlciBldmVudCcpCmBgYAoKCiMjIyBDaGktc3F1YXJlCmBgYHtyfQp2YWx1ZXNfdGVzdCA9IGxheWVyX2RhdGEodmFsdWVzX3Bsb3QsMSkgCnAxID0gcG5vcm0odmFsdWVzX3Rlc3QkeG1heCxtZWFuID0gbWVhbihudW1iZXJfdmFsdWVzKSxzZCA9IHNkKG51bWJlcl92YWx1ZXMpKSAtIHBub3JtKHZhbHVlc190ZXN0JHhtaW4sbWVhbiA9IG1lYW4obnVtYmVyX3ZhbHVlcyksIHNkID0gc2QobnVtYmVyX3ZhbHVlcykpCmBgYAoKYGBge3J9CmNoaXNxLnRlc3QodmFsdWVzX3Rlc3QkY291bnQsIHAgPSBwMSwgcmVzY2FsZS5wID0gVFJVRSkKYGBgCgoKCiMjIEtTLXRlc3QKYGBge3J9CmtzLnRlc3QobnVtYmVyX3ZhbHVlcywicG5vcm0iLG1lYW4obnVtYmVyX3ZhbHVlcyksc2QobnVtYmVyX3ZhbHVlcykpCmBgYAoKCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhLmZyYW1lKHZhbHVlcyA9IG51bWJlcl92YWx1ZXMpLGFlcyhzYW1wbGUgPSB2YWx1ZXMpKSArIHN0YXRfcXEoKSArIHN0YXRfcXFfbGluZShjb2xvciA9ICJyZWQiKSArIGxhYnModGl0bGUgPSAiUVEtIE5vcm0iKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAKYGBgCgo=